import unittest
import numpy as np
import trigonometric
import trigonometric_slow
import penalty_1
import penalty_1_slow
import extended_rosenbrock
import extended_rosenbrock_slow
import extended_fr
import extended_fr_slow


def _show_info(n, c1: object, c2: object):
    return n, c1.__name__, c2.__name__


class TestFunctionImplementation(unittest.TestCase):
    def _check_f_eq(self, n, c1, c2):
        print("F(n=%d): %s <=> %s" % _show_info(n, c1, c2))
        x0, f, g, h = c1.create(n)
        _, f1, g1, h1 = c2.create(n)
        n = x0.shape[0]
        for i in range(100):
            x = np.random.rand(n) * 10.0 - 5.0
            self.assertTrue(np.allclose(f(x), f1(x), rtol=1e-8, atol=1e-12), "Function value mismatch")
            self.assertTrue(np.allclose(g(x), g1(x), rtol=1e-8, atol=1e-12), "Gradient mismatch")
            self.assertTrue(np.allclose(h(x), h1(x), rtol=1e-8, atol=1e-12), "Hessian mismatch")

    def test_trigonometric(self):
        self._check_f_eq(10, trigonometric, trigonometric_slow)

    def test_penalty_1(self):
        self._check_f_eq(20, penalty_1, penalty_1_slow)

    def test_extended_rosenbrock(self):
        self._check_f_eq(20, extended_rosenbrock, extended_rosenbrock_slow)

    def test_extended_fr(self):
        self._check_f_eq(20, extended_fr, extended_fr_slow)


if __name__ == '__main__':
    unittest.main()
